package org.xx.armory.db;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Constructor;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.Callable;

import static org.xx.armory.commons.SysUtils.dumpException;
import static org.xx.armory.commons.Validators.notBlank;

/**
 * 会话工厂的工具类。
 *
 * @author Haart
 */
public final class SessionFactories {
    private static final Logger LOGGER = LoggerFactory.getLogger(SessionFactories.class);
    private static final Map<String, Callable<SessionFactory>> factories = initializeFactoryFactories();

    /**
     * 禁止实例化工具类。
     */
    private SessionFactories() {
        throw new UnsupportedOperationException();
    }

    private static Map<String, Callable<SessionFactory>> initializeFactoryFactories() {
        final String packageName = SessionFactories.class.getPackage().getName();

        final Map<String, Callable<SessionFactory>> ret = new HashMap<>();

        ret.put("derby", registerFactory(packageName + ".impl.DerbySessionFactory"));
        ret.put("mysql", registerFactory(packageName + ".impl.MySqlSessionFactory"));
        ret.put("oracle", registerFactory(packageName + ".impl.OracleSessionFactory"));
        ret.put("postgres", registerFactory(packageName + ".impl.PostgresqlSessionFactory"));

        return ret;
    }

    /**
     * 注册会话工厂。
     *
     * @param factoryName
     *         会话工厂的类名。
     * @return 用于创建会话工厂的工厂。
     * @throws IllegalArgumentException
     *         如果参数{@code factoryName}是{@code null}或者只包含空白字符。
     */
    private static Callable<SessionFactory> registerFactory(
            String factoryName
    ) {
        factoryName = notBlank(factoryName, "factoryName").trim();

        try {
            final Class<? extends SessionFactory> clazz = Class.forName(factoryName).asSubclass(SessionFactory.class);
            final Constructor<? extends SessionFactory> constructor = clazz.getConstructor();
            constructor.setAccessible(true);
            return constructor::newInstance;
        } catch (ReflectiveOperationException | ClassCastException ex) {
            LOGGER.warn("cannot register factory {}, {}", factoryName, dumpException(ex));
            return null;
        }
    }

    /**
     * 根据指定的提供程序名创建会话工厂实例。
     *
     * @param providerName
     *         提供程序名。
     * @return 已创建的会话工厂实例。
     * @throws NullPointerException
     *         如果参数{@code providerName}是{@code null}。
     * @throws IllegalArgumentException
     *         如果参数{@code providerName}只包含空白字符，或者找不到指定的提供程序。
     * @throws IllegalStateException
     *         如果创建会话工厂实例出错。
     */
    public static SessionFactory createFactory(
            String providerName
    ) {
        providerName = notBlank(providerName, "providerName").trim().toLowerCase();

        final Callable<SessionFactory> factoryFactory = factories.getOrDefault(providerName, null);
        if (factoryFactory != null) {
            try {
                return factoryFactory.call();
            } catch (Exception ex) {
                throw new IllegalStateException("cannot create session factory: " + providerName, ex);
            }
        } else {
            throw new IllegalArgumentException("unknown provider: " + providerName);
        }
    }
}
